#ifndef SmartPtr_hpp
#define SmartPtr_hpp

//template <typename T>
class _SPtrCtr // Вспомогательный класс, в котором содержится указатель на T и счётчик количества умных указателей на него
{
public:
   _SPtrCtr(void * ptr) : _ptr(ptr), _counter(1) {}
   //~_SPtrCtr() { delete _ptr; } // Удаление теперь происходит в SmartPtr<T>, т.к. мы тут мым не знаем типа указателя.
   unsigned int get_counter()const { return (this != 0)?_counter : 0; }
   void * get_ptr()const { return (this == 0) ? 0 : _ptr; }
   void inc() { if(this != 0) ++_counter; }
   void dec() { if(this != 0) --_counter; }
private:
   void * _ptr;
   unsigned int _counter;
};

template <typename T>
class SmartPtr
{
public:
   //VL You've killed default contsructor
   //SNMA: fixed.
   template <typename V>
   explicit SmartPtr(V * num) : _ptr(num == 0 ? 0 : new _SPtrCtr(num) ) {}
   SmartPtr() : _ptr(0) {}

   // VL you need to declare copy constructor, cause it has more priority than template constructor 
   //SNMA: OK.
   SmartPtr(SmartPtr<T> & prev) : _ptr(prev._ptr) {_ptr->inc();}
   template <typename V>
   SmartPtr(SmartPtr<V> const & prev) : _ptr(prev._ptr) { /*(*this) = prev;*/ }

   ~SmartPtr(); //VL what's the difference between 'clear_ptr' function and this one
   void clear_ptr(); //SNMA: Fixed. Now destructor calls clear_ptr()


   T & operator * () const { return *(_ptr -> get_ptr()); }
   T * operator -> () const { return _ptr -> get_ptr(); }
   SmartPtr & operator = (SmartPtr const & n);
   //VL better not to use this operator and you have no template operator=
   //SNMA: isn't it fixed now?
   template <typename V>
   SmartPtr & operator = (V * n);
   operator T * () const { return get(); } // VL you've already have getter    //SNMA: OK.
   T * get() const { return (_ptr->get_ptr()); } // VL why not const?           //SNMA: OK.
   
   //VL see, what is safebool;
   //SNMA: смотрел, но не понял. Но из того, что я вроде понял, мне кажется, что нарушение этой идиомы было бы, если бы у меня присутствовал operator bool. Так при чём тут safebool?
   //VL it is really possible that _ptr != 0 and _ptr->_ptr == 0 ?
   bool is_null() const { return (_ptr == 0);} //SNMA: fixed.
   
   template <typename V> friend class SmartPtr;
private:
   _SPtrCtr * _ptr;
};


template <typename T>
SmartPtr<T> & SmartPtr<T>::operator = (SmartPtr const & n)
{
   if(n._ptr != _ptr)
   {
      clear_ptr();
      // VL it's mistake, you don't increment ref //SNMA: fixed.
      // VL use swap-trick here
      _ptr = n._ptr;
      _ptr->inc();
   }
   return * this;
}
/*
template <typename T>
SmartPtr<T> & SmartPtr<T>::operator = (SmartPtr const & n)
{
   if(n._ptr != _ptr)
   {
      // VL use swap-trick here
      swap(SmartPtr<T> (*n), *this); //SNMA: like that?
   }
   return * this;
}
*/

template <typename T>
template <typename V>
SmartPtr<T> & SmartPtr<T> :: operator = (V * n)
{
   if(_ptr->get_ptr() != n)
   {
      clear_ptr();
      _ptr = new _SPtrCtr(n);
   }
   return * this;
}

template <typename T>
SmartPtr<T> :: ~SmartPtr()
{
   clear_ptr();
}

template <typename T>
void SmartPtr<T> :: clear_ptr()
{
   if(_ptr != 0)
   {
      _ptr->dec();
      if(_ptr -> get_counter() == 0)
      {
         delete (T*)(_ptr -> get_ptr());
         delete _ptr;
      }
      _ptr = 0;
   }
}

#endif
